package priv.wangy.micro.common.po.valid.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.util.StringUtils;
import priv.wangy.micro.common.po.valid.IdNum;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.regex.Pattern;

/**
 * @author zhangjq
 * @since 2024-03-06
 */
public class IdNumValidator implements ConstraintValidator<IdNum, String> {

    private int[] age;

    private int gender;

    private static final String CHARACTERS = "0123456789";

    private static final Pattern PATTERN = Pattern.compile("^[0123456789X]+$");

    private static final int[] WEIGHTS = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};

    private static final String CHECK_CODES = "10X98765432";

    public static boolean checkIdNum(String idNum, int gender, int[] age) {
        if (!StringUtils.hasLength(idNum) || idNum.length() != 18 || !PATTERN.matcher(idNum).matches()) {
            return false;
        }

        if (gender >= 0) {
            char g = idNum.charAt(16);
            if ((((int) g - (int) ('0')) % 2) != (gender % 2)) {
                return false;
            }
        }

        if (age[0] > 0 || age[1] > 0) {
            LocalDate birth = LocalDate.parse(idNum.substring(6, 14), DateTimeFormatter.ofPattern("yyyyMMdd"));
            LocalDate now = LocalDate.now();
            long dif = ChronoUnit.YEARS.between(birth, now);
            if (age[0] > 0 && dif < age[0]) {
                return false;
            }
            if (age[1] > 0 && dif > age[1]) {
                return false;
            }
        }

        int sum = 0;
        for (int i = 0; i < 17; i++) {
            char c = idNum.charAt(i);
            int w = WEIGHTS[i];
            int v = CHARACTERS.indexOf(c);
            sum += w * v;
        }

        char cc = CHECK_CODES.charAt(sum % 11);
        char lc = idNum.charAt(17);
        return cc == lc;
    }

    @Override
    public void initialize(IdNum constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
        age = constraintAnnotation.age();
        gender = constraintAnnotation.gender();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return checkIdNum(value.toUpperCase(), gender, age);
    }
}
